package cn.lingyangwl.agile.auth.oauth2.config;

import cn.lingyangwl.agile.common.security.annotation.Anonymous;
import cn.lingyangwl.agile.common.security.annotation.InnerAuth;
import cn.lingyangwl.agile.common.security.utils.RequestMappingUtils;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 设置Anonymous注解允许匿名访问的url
 *
 * @author shenguangyang
 */
//@ConfigurationProperties(prefix = PROPERTIES_PREFIX + "security.oauth2")
public class MySecurityOauth2Properties implements InitializingBean, ApplicationContextAware {
    private static final String[] DEFAULT_IGNORE_URLS = new String[] {
            "/actuator/**", "/error", "/oauth2/token/**",
            "/v3/api-docs", "/v2/api-docs",
            "/favicon.ico"
    };

    private ApplicationContext applicationContext;

    @Getter
    @Setter
    private Set<String> excludePaths = new HashSet<>();

    @Value("${server.servlet.context-path:}")
    private String baseUrl;

    @Override
    public void afterPropertiesSet() {
        excludePaths.addAll(Arrays.stream(DEFAULT_IGNORE_URLS).filter(StringUtils::isNotEmpty).collect(Collectors.toList()));

        Map<String, RequestMappingHandlerMapping> beansOfType = applicationContext.getBeansOfType(RequestMappingHandlerMapping.class);
        beansOfType.values().forEach(e -> {
            Map<RequestMappingInfo, HandlerMethod> map = e.getHandlerMethods();
            map.keySet().forEach(info -> {
                HandlerMethod handlerMethod = map.get(info);

                // 获取方法上边的注解 替代path variable 为 *
                Anonymous methodOfAnonymous = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class);
                excludePaths.addAll(RequestMappingUtils.getMappingPath(methodOfAnonymous, info));

                // 获取内部接口, 不需要鉴权
                InnerAuth methodOfInnerAuth = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), InnerAuth.class);
                if (Objects.nonNull(methodOfInnerAuth) && !methodOfInnerAuth.verifyUser()) {
                    excludePaths.addAll(RequestMappingUtils.getMappingPath(methodOfInnerAuth, info));
                }

                // 获取类上边的注解, 替代path variable 为 *
                Anonymous controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Anonymous.class);
                excludePaths.addAll(RequestMappingUtils.getMappingPath(controller, info));
            });
        });
    }

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        this.applicationContext = context;
    }
}
